#include "MeasConnectionHandler.h"
#include "Engine.h"
#include <boost/algorithm/string.hpp>

namespace core {

MeasConnection::~MeasConnection() {
    if(!connTypes->empty())
        connTypes->clear();

    delete connTypes;
}

MeasConnectionHandler::MeasConnectionHandler() {
}

MeasConnectionHandler::~MeasConnectionHandler() {
    for(std::list<MeasConnection*>::iterator it = connections.begin();
        it != connections.end(); ++it) {
        delete *it;
    }

    connections.clear();
}

bool MeasConnectionHandler::matchTypes(boost::shared_ptr<measurement::MeasurementObject> from,
                                       boost::shared_ptr<measurement::MeasurementObject> to,
                                       ConnectionTypes** connTypes) {
    if(from->outputType == L"" || to->inputType == L"")
        return false;

    // accept anything?
    if(to->inputType == L"*") {
        *connTypes = new ConnectionTypes;
        (*connTypes)->push_back(L"*");
        return true;
    }

    std::list<std::wstring> inTypes, outTypes;
    boost::algorithm::split(outTypes, from->outputType, boost::is_any_of(L";"));
    boost::algorithm::split(inTypes, to->inputType, boost::is_any_of(L";"));

    for(std::list<std::wstring>::const_iterator otIt = outTypes.begin();
        otIt != outTypes.end(); ++otIt) {

        for(std::list<std::wstring>::const_iterator inIt = inTypes.begin();
            inIt != inTypes.end(); ++inIt) {

            if((*otIt) == (*inIt)) {
                if(*connTypes == NULL)
                    *connTypes = new ConnectionTypes;
                (*connTypes)->push_back(*inIt);
            } else {
                std::wstring::size_type outPos = (*otIt).find_first_of(L"*");
                std::wstring::size_type inPos = (*inIt).find_first_of(L"*");
                std::wstring modifiedIn, modifiedOut;

                if(outPos != std::wstring::npos)
                    modifiedOut = (*otIt).substr(0, outPos);
                else modifiedOut = (*otIt);

                if(inPos != std::wstring::npos)
                    modifiedIn = (*inIt).substr(0, inPos);
                else modifiedIn = (*inIt);

                if(outPos < inPos) {
                    modifiedIn = (*inIt).substr(0, outPos);
                } else if(inPos < outPos) {
                    modifiedOut = (*otIt).substr(0, inPos);
                }

                if(modifiedIn == modifiedOut) {
                    if(*connTypes == NULL)
                        *connTypes = new ConnectionTypes;
                    (*connTypes)->push_back(modifiedIn);
                }
            }
        }
    }

    // connection found if "connTypes" is no longer NULL
    return *connTypes != NULL;
}

void MeasConnectionHandler::connect(boost::shared_ptr<measurement::MeasBase> measBase) {
    const MeasBaseStorage& moList = ConfigurationManager::getInstance().getLoadedMOs();

    MeasConnection* connection;
    ConnectionTypes* connTypes;
    boost::shared_ptr<measurement::MeasurementObject> mo = measBase->getMOLink();
    boost::shared_ptr<measurement::MeasurementObject> current;

    for(MeasBaseStorage::const_iterator it = moList.begin();
        it != moList.end(); ++it) {

        current = (*it)->getMOLink();

        if(current->index == mo->index)
            continue;

        connTypes = NULL;

        /**
         * Connect:
         * DataSource to ( Visualization or DataProcessor )
         * DataProcessor to ( Visualization )
         * Visualization to ( DataSource or DataProcessor )
         */
        if((mo->type == measurement::DataSource &&
                ( current->type == measurement::Visualization || current->type == measurement::DataProcessor )) ||
           (mo->type == measurement::DataProcessor && current->type == measurement::Visualization)) {

            if(matchTypes(mo, current, &connTypes)) {
                    connection = new MeasConnection;
                    connection->first = WeakMOPtr(measBase);
                    connection->second = WeakMOPtr(*it);
                    connection->connTypes = connTypes;

                    connections.push_back(connection);
            }
        } else if(mo->type == measurement::Visualization &&
                  ( current->type == measurement::DataSource || current->type == measurement::DataProcessor)) {

            if(matchTypes(current, mo, &connTypes)) {
                connection = new MeasConnection;
                connection->first = WeakMOPtr(*it);
                connection->second = WeakMOPtr(measBase);
                connection->connTypes = connTypes;

                connections.push_back(connection);
            }
        }
    }
}

void MeasConnectionHandler::disconnect(boost::shared_ptr<measurement::MeasBase> mo) {
    std::list<MeasConnection*> tempConnections;

    for(std::list<MeasConnection*>::iterator it = connections.begin();
        it != connections.end(); ++it) {

        SharedMOPtr from = (*it)->first.lock();
        SharedMOPtr to = (*it)->second.lock();

        if(from && to) {
            if(from->getMeasIndex() == mo->getMeasIndex() ||
               to->getMeasIndex() == mo->getMeasIndex()) {

                delete *it;
            }
            else
                tempConnections.push_back(*it);
        } else {
            delete *it;
        }
    }

    connections.clear();
    connections = tempConnections;
}

ConnectionsList &MeasConnectionHandler::getConnections() {
    return connections;
}

}
